﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Dapper;

namespace System.Data {
    public class BaseRepository<TEntity>:IRepository<TEntity> where TEntity : Entity {
        private IDbSession _dbSession = null;
        public BaseRepository() {
            _dbSession = DbFactory.DbHelper();
        }
        public IDbSession DBA { get { return _dbSession; } }
        public string TableName { get { return _dbSession?.GetDbTableName<TEntity>(); } }
        #region Insert
        public virtual TEntity Add(TEntity model) {
            if(model == null) throw new ArgumentNullException(nameof(model));
            if(string.IsNullOrWhiteSpace(model.ID)) model.ID = UUIDUtils.Fid;
            model.Create();
            var insert = _dbSession?.Insert(model);
            model.ID = insert;
            return model;
        }
        public void Add(List<TEntity> models) {
            foreach(var item in models) {
                item.Create();
                _dbSession?.Insert(item);
            }
        }
        public TEntity AddOrUpdate(TEntity model) {
            if(model == null) throw new ArgumentNullException(nameof(model));
            if(!string.IsNullOrWhiteSpace(model.ID) && Exists(model.ID)) {
                Update(model);
                return model;
            } else {
                Add(model);
                return model;
            }
        }
        #endregion

        #region Update
        public TEntity Update(TEntity model) {
            if(model == null) throw new ArgumentNullException(nameof(model));
            model.Modify();
            var insert = _dbSession.Update(model);
            return Find(model.ID);
        }
        public void Update(List<TEntity> models) {
            foreach(var item in models) {
                item.Modify();
                _dbSession?.Update(item);
            }
        }
        public bool UpdateField(string keyId,string fieldName,string fieldValue) {
            if(string.IsNullOrWhiteSpace(keyId)) throw new ArgumentNullException(nameof(keyId));
            if(string.IsNullOrWhiteSpace(fieldName)) throw new ArgumentNullException(nameof(fieldName));
            if(string.IsNullOrWhiteSpace(fieldValue)) throw new ArgumentNullException(nameof(fieldValue));

            string sql = string.Format(@"update [" + _dbSession?.GetDbTableName<TEntity>() + "] set {0}='{1}' where ID='{2}'",fieldName,fieldValue,keyId);
            int result = _dbSession.ExecuteNonQuery(sql);
            return result > 0;
        }
        #endregion

        #region Delete
        public void Delete(TEntity model) {
            model.Remove();
            var softDelete = this as ISoftDelete;
            if(softDelete != null && softDelete.IsDelete == 1) {
                _dbSession?.Update<TEntity>(model);
            } else {
                _dbSession?.Delete<TEntity>(model);
            }
        }
        /// <summary>
        /// 批量删除数据 1,2,3
        /// </summary>
        public virtual bool Delete(string Idlist) {
            if(string.IsNullOrWhiteSpace(Idlist)) throw new ArgumentNullException(nameof(Idlist));
            var idarr = Idlist.Split(",");
            StringBuilder strSql = new StringBuilder();
            strSql.Append("delete from [" + _dbSession?.GetDbTableName<TEntity>() + "] ");
            strSql.Append(" where Id in (" + idarr.ToSql(false) + ")  ");
            int result = _dbSession.ExecuteNonQuery(strSql.ToString());
            if(result > 0) {
                return true;
            } else {
                return false;
            }
        }
        public bool Delete(Expression<Func<TEntity,bool>> predicate) {
            var queryResult = DynamicQuery.GetDynamicQuery(_dbSession?.GetDbTableName<TEntity>(),predicate);
            var result = _dbSession.ExecuteNonQuery(queryResult.Sql,(object)queryResult.Param);
            return result > 0;
        }
        public bool Delete(List<TEntity> models) {
            if(models == null) throw new ArgumentNullException(nameof(models));
            foreach(var item in models) {
                item.Remove();
                var softDelete = this as ISoftDelete;
                if(softDelete != null && softDelete.IsDelete == 1) {
                    _dbSession?.Update<TEntity>(item);
                } else {
                    _dbSession?.Delete<TEntity>(item);
                }
            }
            return true;
        }
        public bool DeleteWhere(string sqlWhere) {
            if(string.IsNullOrWhiteSpace(sqlWhere)) throw new ArgumentNullException(nameof(sqlWhere));
            string sql = string.Format("delete [{0}] where {1}",_dbSession?.GetDbTableName<TEntity>(),sqlWhere);
            int result = _dbSession.ExecuteNonQuery(sql);
            return result > 0;
        }
        #endregion

        #region Exists
        public bool Exists(string id) {
            if(string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id));
            var allrecords = _dbSession.Get<TEntity>(id);
            return allrecords != null;
        }
        public bool Exists(ISpecification<TEntity> specification) {
            var query = specification.GetExpression();
            return Exists(query);
        }
        public bool Exists(Expression<Func<TEntity,bool>> predicate) {
            var queryResult = DynamicQuery.GetDynamicQuery(_dbSession?.GetDbTableName<TEntity>(),predicate);
            var result = _dbSession.ExecuteScalar(queryResult.Sql,(object)queryResult.Param);
            return result != null;
        }
        #endregion

        #region Find
        public TEntity Find(string id) {
            var result = _dbSession?.Get<TEntity>(id);
            return result;
        }
        public TEntity Find(Expression<Func<TEntity,bool>> predicate) {
            return _dbSession?.GetFirstOrDefault(predicate);
        }
        public TEntity Find(ISpecification<TEntity> specification) {
            var query = specification.GetExpression();
            return Find(query);
        }
        #endregion

        #region FindAll
        public IQueryable<TEntity> FindAll() {
            return FindAll(string.Empty);
        }
        public IQueryable<TEntity> FindAll(Expression<Func<TEntity,bool>> predicate) {
            return _dbSession?.GetList(predicate).OrderBy(u => u.OrderBy).AsQueryable();
        }
        public IQueryable<TEntity> FindAll(string sqlWhere) {
            StringBuilder strSql = new StringBuilder();
            strSql.Append("SELECT * FROM [" + _dbSession?.GetDbTableName<TEntity>() + "]");
            if(!string.IsNullOrWhiteSpace(sqlWhere)) {
                strSql.Append(" WHERE " + sqlWhere);
            }
            var tt = _dbSession.Query<TEntity>(strSql.ToString());
            return tt.OrderBy(u => u.OrderBy).AsQueryable();
        }
        public JQridPageData FindAll(Paging paging) {
            int total = 0;
            string tableName = _dbSession?.GetDbTableName<TEntity>();
            var list = _dbSession?.GetPageSql<TEntity>(paging.field,tableName,paging.sqlwhere,paging.orderby,paging.page,paging.rows,out total);
            JQridPageData pageData = new JQridPageData();
            pageData.rows = list;
            pageData.page = paging.page;
            pageData.pageRows = paging.rows;
            pageData.records = total;
            return pageData;
        }
        public IQueryable<TEntity> FindAll(string sqlWhere,string orderby = "OrderBy desc",int startIndex = 1,int endIndex = 10) {
            int i_inde, i_end;
            i_end = startIndex * endIndex;
            i_inde = (startIndex - 1) * endIndex + 1;
            string tableName = _dbSession?.GetDbTableName<TEntity>();
            StringBuilder strSql = new StringBuilder();
            strSql.Append("SELECT * FROM ( ");
            strSql.Append(" SELECT ROW_NUMBER() OVER (");
            if(!string.IsNullOrEmpty(orderby)) {
                strSql.Append("order by T." + orderby);
            } else {
                strSql.Append("order by T.Id desc");
            }
            strSql.Append(")AS Row, T.*  from [" + tableName + "] T ");
            if(!string.IsNullOrEmpty(sqlWhere)) {
                strSql.Append(" WHERE " + sqlWhere);
            }
            strSql.Append(" ) TT");
            strSql.AppendFormat(" WHERE TT.Row between {0} and {1}",i_inde,i_end);
            var tt = _dbSession.Query<TEntity>(strSql.ToString());
            return tt.AsQueryable();
        }
        #endregion
    
        /// <summary>
        /// 执行事务
        /// <code>
        ///  TransactionProxy((connection, transaction) =>
        ///  {
        ///   var columns = connection.Query<FapColumn>("select * from FapColumn where    TableName=@TableName", new { TableName = table.TableName }, transaction);
        /// 
        /// });
        /// </code>
        /// </summary>
        /// <param name="execAction"></param>
        public void TransactionProxy(Action<IDbConnection,IDbTransaction> execAction) {
            using var connection = _dbSession.CreateConnectionAndOpen();
            if(connection.State == ConnectionState.Closed) {
                connection.Open();
            }
            using var transaction = connection.BeginTransaction();
            try {
                execAction?.Invoke(connection,transaction);
                transaction.Commit();
            } catch(Exception ex) {
                transaction.Rollback();
                throw new Exception($"事务执行异常:{ex.Message}",ex);
            }
        }

    }
}
